//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include "ccommand.h"
#include "cinterpreter.h"
#include <buola/os/cprocess.h>
#include <buola/os/cenv.h>
#include <buola/os/path.h>
#include <buola/io.h>
#include <stack>
#include <buola/io/glob.h>
#include <buola/python/types.h>
#include <buola/io/cindirectbufferstream.h>

namespace buola { namespace bush {
    
void CCommand::SetOutput(std::string *pOut)
{
    mOutput=pOut;
}

void CCommand::ExpandArgs()
{
    for(int i=0;i<mParsedWords.size();i++)
    {
        std::vector<CWord> lWords;
        mParsedWords[i].ExpandMultiple(lWords);
        
        for(int j=0;j<lWords.size();j++)
            mWords.push_back(std::move(lWords[j]));
    }

    mWords[0].GetUnescaped(mCommand);
    mArgs.resize(mWords.size()-1);
    
    for(int i=0;i<mArgs.size();i++)
        mWords[i+1].GetUnescaped(mArgs[i]);
    
    //check whether it is a builtin command
    mBuiltin=gInterpreter->GetBuiltin(mCommand);
}
    
void CCommand::CreateProcess(PCommand pNext)
{
    if(pNext)
    {
        mNextCommand=pNext.get();
        io::CPipe lPipe;
        lPipe.Create();
        Prepend(new CRedirection(1,lPipe.GetOutputFD(),TOKEN_OP_OR));
        pNext->Prepend(new CRedirection(0,lPipe.GetInputFD(),TOKEN_OP_OR));
    }
    else 
    {
        mNextCommand=nullptr;
        if(mOutput)
        {
            io::CPipe lPipe;
            lPipe.Create();
            Prepend(new CRedirection(1,lPipe.GetOutputFD(),TOKEN_OP_OR));
            mOutStream=new io::CIndirectBufferStream(lPipe.GetInput());
        }
    }
    
    if(mBuiltin)
        return;
    
    io::CURI lCommand=os::get_command(mCommand);
    if(lCommand.empty())
        throw XNotFound("command not found");

    mProcess.reset(new CProcess(lCommand,mArgs,CProcess::OPTION_NONE));
    mProcess->SetCmdArg(mCommand);

    for(int i=0;i<mRedirections.size();i++)
    {
        mRedirections[i]->Apply(*mProcess);
    }
}

int CCommand::Launch(int pPGID,bool pFG)
{
    if(mBuiltin)
    {
        int lPGID;
        if(mNextCommand)
            lPGID=mNextCommand->Launch(pPGID,pFG);
        
        io::PFDStream lStream;

        for(int i=0;i<mRedirections.size();i++)
            lStream=mRedirections[i]->GetOutputStream();

        io::PBufferStream lOut;
        if(lStream)
            lOut=new io::CIndirectBufferStream(lStream);
        else
            lOut=gOut.Stream();
        
        mBuiltin->Run(mArgs,lOut);
        return lPGID;
    }
    
    if(pPGID)
    {
        mProcess->SetProcessGroup(pPGID);
    }
    else
    {
        mProcess->SetProcessGroup(CProcess::GROUP_OWN);
        if(pFG)
            mProcess->SetOption(CProcess::SET_TTY_PGRP);
    }

    mProcess->Launch();
    
    if(!pPGID)
    {
        pPGID=mProcess->GetPID();
    }

    if(mOutStream)
    {
        std::string lBuffer;
        while(io::get_line(mOutStream,lBuffer))
        {
            mOutput->append(lBuffer);
            *mOutput+='\n';
        }
        mOutStream=nullptr;
    }
    
    if(mNextCommand)
        return mNextCommand->Launch(pPGID,pFG);
    
    return pPGID;
}

void CCommand::Wait()
{
    if(mProcess)
    {
        mProcess->Wait(false);
    }
}

CProcess::EStatus CCommand::GetStatus()
{
    if(mProcess)
    {
        return mProcess->GetStatus();
    }
    
    return CProcess::STATUS_TERMINATED;
}

int CCommand::GetExitCode()
{
    if(mProcess)
    {
        return mProcess->GetExitCode();
    }
    
    return 0;
}

int CCommand::GetExitSignal()
{
    if(mProcess)
    {
        return mProcess->GetExitSignal();
    }
    
    return 0;
}

bool CCommand::Signalled()
{
    if(mProcess)
    {
        return mProcess->Signalled();
    }
    
    return false;
}

/*namespace bush*/ } /*namespace buola*/ }
